#include "FKUser.h"
#include "FKRoom.h"
#include "FKClient.h"

#include <QSettings>
#include <QCoreApplication>
#include <QDateTime>

#include "FKEventNames.h"

#include "FKLogger.h"
#include "FKPaths.h"
#include "FKMessageType.h"

FK_EVENTS(FKUser){
    events.insert(FKEventName::e_exitRoom,&FKUser::E_exitRoom);
    events.insert(FKEventName::e_privateMessage,&FKUser::E_privateMessage);
    events.insert(FKEventName::e_commandMessage,&FKUser::E_commandMessage);
    events.insert(FKEventName::e_chatErrorMessage,&FKUser::E_chatErrorMessage);
    events.insert(FKEventName::e_enterRoom,&FKUser::E_enterRoom);

    actions.insert(FKEventName::a_setName,&FKUser::A_setName);
    actions.insert(FKEventName::a_enterRoom,&FKUser::A_enterRoom);
    actions.insert(FKEventName::a_createRoom,&FKUser::A_createRoom);
    actions.insert(FKEventName::a_exitRoom,&FKUser::A_exitRoom);
    actions.insert(FKEventName::a_message,&FKUser::A_message);

    props<<FKEventName::c_name;
}

QHash<QString,FKUser*> FKUser::_users=QHash<QString,FKUser*>();
QStringList FKUser::_nameReestr=QStringList();
QString FKUser::_clientType=QString();

FKUser::FKUser(FKRoom *room, qint32 objectID):FKObject(room,objectID){
    FK_CBEGIN
    allowEmitBy(this);
    FK_CEND
}

FKUser::~FKUser(){
    FK_DBEGIN
    if(_userName!=QString())_users.remove(_userName);
    FK_DEND
}

void FKUser::manageEvent(FKEvent* event){
//    FK_MLOGV("user got event",event->subject())
    if(room()){
        room()->enqueEvent(event);
    }else{
        if(event->object()==getID()){
//            FK_MLOGV("user process event",event->subject())
            processFKEvent(event);
        }else{
            FKObject* obj=getObject(event->object());
            if(obj/* && sharedWatcheds().contains(obj)*/){
                obj->processFKEvent(event);
            }else{
                FKEvent* errorEvent=FKEvent::errorEvent(event);
                event->deleteLater();
                emit newEvent(errorEvent);
            }
        }
    }
}

void FKUser::manageAction(FKEvent* action){
    if(room()){
        room()->enqueAction(action);
    }else{
        if(action->object()==getID()){
            processFKAction(action);
        }else{
//            FKObject* obj=getObject(action->object());
//            if(obj && sharedWatcheds().contains(obj)){
//                obj->processFKAction(action);
//            }else{
            FKEvent* errorEvent=FKEvent::errorEvent(action);
            action->deleteLater();
            emit newEvent(errorEvent);
//            }
        }
    }
}

void FKUser::setNameReestr(QString clientType){
    _clientType=clientType;
    QSettings settings;
    _nameReestr=settings.value(FKPaths::userReestr+clientType).toStringList();
}

QStringList FKUser::nameReestr(){
    return _nameReestr;
}

bool FKUser::isInReestr(QString userName){
    return _nameReestr.contains(userName);
}

bool FKUser::addToReestr(QString userName){
    if(!_nameReestr.contains(userName)){
        _nameReestr.append(userName);
        QSettings settings;
        settings.setValue(FKPaths::userReestr+_clientType,_nameReestr);
        return true;
    }else{
        return false;
    }
}

bool FKUser::removeFromReestr(QString userName){
    if(_nameReestr.contains(userName)){
        _nameReestr.removeOne(userName);
        QSettings settings;
        settings.setValue(FKPaths::userReestr+_clientType,_nameReestr);
        return true;
    }else{
        return false;
    }
}

QString FKUser::userName() const{
    return _userName;
}
bool FKUser::setName(QString userName){
    if(_userName==userName){
        return false;
    }else if(_users.contains(userName)){
        FK_MLOGV("username already in use",getID())
        return false;
    }else{
        if(_userName!=QString())_users.remove(_userName);
        _userName=userName;
        _users[userName]=this;
        return true;
    }
}

void FKUser::A_setName(FKEvent* event){
    if(setName(event->value().toString())){
        updateProp(FKEventName::c_name);
        applyUpdates();
    }
}

bool FKUser::isInIgnore(FKUser* u) const{
    Q_UNUSED(u)
    return false;
}

void FKUser::A_message(FKEvent* event){
    QString message(event->value().toString());
    QString reciever;
    QString messageType(parseMessage(message, reciever));
    if(messageType==FKMessageType::publicType){
        publicMessage(message);
    }else if(messageType==FKMessageType::privateType){
        privateMessage(message, reciever);
    }else if(messageType==FKMessageType::errorType){
        chatErrorMessage(QString(tr("Invalid command")));
    }
}

QString FKUser::parseMessage(QString &message, QString &reciever){
    if(message.isEmpty()){
        return FKMessageType::emptyType;
    }else if(message[0]!='/'){
        return FKMessageType::publicType;
    }else{
        QStringList a(message.split(QChar(' ')));
        if(a[0]==QString("/w") && a.size()>2){
            a.removeFirst();
            reciever=a.takeFirst();
            message=a.join(QChar(' '));
            return FKMessageType::privateType;
        }else{
            return FKMessageType::errorType;
        }
    }
}

void FKUser::publicMessage(QString message){
    if(room()){
        doEvent(room(),FKEventName::e_publicMessage,QString("%1 (%3): %2").arg(userName()).arg(message).arg(QTime::currentTime().toString(QString("hh:mm"))),FKObject::ToClientsAndServer,getID());
    }else{
        FK_MLOGV("user has no room for public message",message)
    }
}

void FKUser::privateMessage(QString message, QString reciever){
    FKUser* u=_users.value(reciever,0);
    if(u){
        if(u->isInIgnore(this)){
            chatErrorMessage(QString(tr("User ignore you, message was not delivered")));
        }else{
            doEvent(u,FKEventName::e_privateMessage,QString("%1 (%3): %2").arg(userName()).arg(message).arg(QTime::currentTime().toString(QString("hh:mm"))),FKObject::ToClientsAndServer,u->getID());
            doEvent(this,FKEventName::e_commandMessage,QString("%1: %2").arg(reciever).arg(message),FKObject::ToClientsAndServer,getID());
        }
    }else{
        chatErrorMessage(QString(tr("Cannot find user")));
    }
}

void FKUser::chatErrorMessage(QString message){
    doEvent(this,FKEventName::e_chatErrorMessage,message,FKObject::ToClientsAndServer,getID());
}

void FKUser::E_privateMessage(FKEvent* event){
    QString message(event->value().toString());
    emit privateMessageRecieved(message);
}

void FKUser::E_commandMessage(FKEvent* event){
    QString message(event->value().toString());
    emit commandMessageRecieved(message);
}

void FKUser::E_chatErrorMessage(FKEvent* event){
    QString message(event->value().toString());
    emit chatErrorMessageRecieved(message);
}

FKUser* FKUser::catchUser(QString userName){
    FKUser* u=_users.value(userName,0);
    if(!u){
        u=new FKUser;
        u->setName(userName);
        u->updateProp(FKEventName::c_name);
        u->applyUpdates();
    }
    return u;
}

//void FKUser::setConnected(){
//    if(room()){
//        room()->userConnected(this);
//        room()->refreshActorsModel(this);
//    }
//}

void FKUser::setDisconnected(){
    if(room())room()->userDisconnected(this);
}

void FKUser::A_enterRoom(FKEvent* event){
    qint32 roomID(event->value().toInt());
    enterRoom_A(roomID);
}

void FKUser::enterRoom_A(qint32 roomID){
    FKRoom* room=qobject_cast<FKRoom*>(getObject(roomID));
    if(room){
        if(!_client->checkRoomAvaliable(room->getClassName())){
            doEvent(QString(tr("This room type is not avaliable for your application")),this);
        }else if(room->started()){
            doEvent(QString(tr("Room already started")),this);
        }else if(this->room()){
            doEvent(QString(tr("User already in room")),this);
        }else{
            setCustomWatching(room);
    //        room->revealWatching(this);
    //        room->allowControlBy(this,FKEventName::A_sceneLoadingComplete);
            room->allowControlBy(this,FKEventName::a_clientLoadingComplete);
            room->addUser(this);
            enterRoom(room);
            doEvent(this,FKEventName::e_enterRoom,roomID,FKObject::ToDistantClients,getID());
        }
    }else{
        doEvent(QString(tr("Invalid room ID")),this);
    }
}

void FKUser::E_enterRoom(FKEvent* event){
//    FK_MLOG("room entered1")
    qint32 roomID(event->value().toInt());
    FKRoom* room=qobject_cast<FKRoom*>(getObject(roomID));
    if(room){
//        FK_MLOG("room entered2")
        enterRoom(room);
    }else{
        FK_MLOGV("cannot find room for enter",roomID)
    }
}

void FKUser::enterRoom(FKRoom* room){
    setRoom(room);
    emit roomChosen(room);
//    emit newEvent(FKEvent::errorEvent(QString("Room entered"),sharedVisors().toList().at(0)->getID()));
}

void FKUser::A_createRoom(FKEvent* event){
    QStringList roomData(event->value().toStringList());
    FKRoom* r=FKRoom::createRoom(roomData);
    if(r){
        r->setChatLogFile(qApp->applicationDirPath()+"/../RoomLogs/"+r->roomType()+QString::number(r->getID())+"-"+QDateTime::currentDateTime().toString(QString("hh-mm-ss-dd-MM-yy"))+".log");
        r->allowControlBy(this,FKEventName::a_beginLoading);
        enterRoom_A(r->getID());
    }else{
        doEvent(QString(tr("Cannot create room")),this);
    }
}

void FKUser::A_exitRoom(FKEvent* event){
    Q_UNUSED(event)
    if(room()){
        room()->removeUser(this);
        setRoom(0);
        emit roomChosen(0);
        doEvent(this,FKEventName::e_exitRoom,QVariant(),FKObject::ToDistantClients,getID());
        stopTotalWatching();
    }
}

void FKUser::E_exitRoom(FKEvent *event){
    Q_UNUSED(event)
    setRoom(0);
    emit roomChosen(0);
}
